using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Matrix.Notification.Domain.Notifications;
using Matrix.Notification.Domain.Shared.Notifications;

namespace Matrix.Notification.EntityFrameworkCore.Notifications
{
    /// <summary>
    /// 通知订阅仓储实现
    /// </summary>
    public class NotificationSubscriptionRepository : EfCoreRepository<NotificationDbContext, NotificationSubscription, Guid>, INotificationSubscriptionRepository
    {
        public NotificationSubscriptionRepository(IDbContextProvider<NotificationDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public async Task<NotificationSubscription?> GetWithDetailsAsync(
            Guid id,
            CancellationToken cancellationToken = default)
        {
            return await DbSet
                .IncludeDetails()
                .FirstOrDefaultAsync(x => x.Id == id, GetCancellationToken(cancellationToken));
        }

        public async Task<NotificationSubscription?> GetByUserIdAsync(
            Guid userId,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .FirstOrDefaultAsync(x => x.UserId == userId, GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetListAsync(
            string? sorting = null,
            int maxResultCount = int.MaxValue,
            int skipCount = 0,
            string? filter = null,
            bool? isEnabled = null,
            bool? isGloballyDisabled = null,
            bool? allowMarketing = null,
            bool? allowSystem = null,
            bool? allowSecurity = null,
            NotificationPriority? priority = null,
            string? languagePreference = null,
            DateTime? lastActiveSince = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .WhereIf(!filter.IsNullOrWhiteSpace(), x =>
                    x.UserName.Contains(filter!) ||
                    x.EmailAddress!.Contains(filter!) ||
                    x.PhoneNumber!.Contains(filter!))
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .WhereIf(isGloballyDisabled.HasValue, x => x.IsGloballyDisabled == isGloballyDisabled.Value)
                .WhereIf(allowMarketing.HasValue, x => x.AllowMarketingNotifications == allowMarketing.Value)
                .WhereIf(allowSystem.HasValue, x => x.AllowSystemNotifications == allowSystem.Value)
                .WhereIf(allowSecurity.HasValue, x => x.AllowSecurityNotifications == allowSecurity.Value)
                .WhereIf(priority.HasValue, x => x.DefaultPriority == priority.Value)
                .WhereIf(!languagePreference.IsNullOrWhiteSpace(), x => x.LanguagePreference == languagePreference)
                .WhereIf(lastActiveSince.HasValue, x => x.LastActiveTime >= lastActiveSince.Value)
                .OrderBy(sorting ?? nameof(NotificationSubscription.UserName))
                .PageBy(skipCount, maxResultCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<long> GetCountAsync(
            string? filter = null,
            bool? isEnabled = null,
            bool? isGloballyDisabled = null,
            bool? allowMarketing = null,
            bool? allowSystem = null,
            bool? allowSecurity = null,
            NotificationPriority? priority = null,
            string? languagePreference = null,
            DateTime? lastActiveSince = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .WhereIf(!filter.IsNullOrWhiteSpace(), x =>
                    x.UserName.Contains(filter!) ||
                    x.EmailAddress!.Contains(filter!) ||
                    x.PhoneNumber!.Contains(filter!))
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .WhereIf(isGloballyDisabled.HasValue, x => x.IsGloballyDisabled == isGloballyDisabled.Value)
                .WhereIf(allowMarketing.HasValue, x => x.AllowMarketingNotifications == allowMarketing.Value)
                .WhereIf(allowSystem.HasValue, x => x.AllowSystemNotifications == allowSystem.Value)
                .WhereIf(allowSecurity.HasValue, x => x.AllowSecurityNotifications == allowSecurity.Value)
                .WhereIf(priority.HasValue, x => x.DefaultPriority == priority.Value)
                .WhereIf(!languagePreference.IsNullOrWhiteSpace(), x => x.LanguagePreference == languagePreference)
                .WhereIf(lastActiveSince.HasValue, x => x.LastActiveTime >= lastActiveSince.Value)
                .LongCountAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetSubscribersByTypeAsync(
            NotificationType notificationType,
            NotificationChannel? channel = null,
            bool onlyEnabled = true,
            CancellationToken cancellationToken = default)
        {
            var queryable = await GetQueryableAsync();

            queryable = queryable
                .IncludeDetails()
                .Where(x => x.CanReceiveNotification(notificationType, NotificationChannel.InApp)) // 基础检查
                .WhereIf(onlyEnabled, x => x.IsEnabled && !x.IsGloballyDisabled)
                .WhereIf(channel.HasValue, x => x.CanReceiveNotification(notificationType, channel.Value));

            return await queryable
                .OrderBy(x => x.UserName)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetSubscribersByChannelAsync(
            NotificationChannel channel,
            bool onlyEnabled = true,
            CancellationToken cancellationToken = default)
        {
            var queryable = await GetQueryableAsync();

            queryable = queryable
                .IncludeDetails()
                .Where(x => x.CanReceiveNotification(NotificationType.System, channel)) // 使用系统类型作为基础检查
                .WhereIf(onlyEnabled, x => x.IsEnabled && !x.IsGloballyDisabled);

            return await queryable
                .OrderBy(x => x.UserName)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetSubscribersByCategoryAsync(
            string category,
            NotificationChannel? channel = null,
            bool onlyEnabled = true,
            CancellationToken cancellationToken = default)
        {
            var queryable = await GetQueryableAsync();

            queryable = queryable
                .IncludeDetails()
                .Where(x => x.GetSubscribedCategories().Contains(category))
                .WhereIf(onlyEnabled, x => x.IsEnabled && !x.IsGloballyDisabled)
                .WhereIf(channel.HasValue, x => x.CanReceiveNotification(NotificationType.System, channel.Value));

            return await queryable
                .OrderBy(x => x.UserName)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetInactiveSubscribersAsync(
            DateTime inactiveSince,
            int maxCount = 100,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => !x.LastActiveTime.HasValue || x.LastActiveTime < inactiveSince)
                .Where(x => x.IsEnabled)
                .OrderByDescending(x => x.LastActiveTime)
                .Take(maxCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetGlobalDisabledSubscribersAsync(
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.IsGloballyDisabled)
                .OrderBy(x => x.GlobalDisabledTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetExpiredGlobalDisabledSubscribersAsync(
            CancellationToken cancellationToken = default)
        {
            var now = DateTime.UtcNow;
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.IsGloballyDisabled &&
                           x.GlobalDisableExpiryTime.HasValue &&
                           x.GlobalDisableExpiryTime < now)
                .OrderBy(x => x.GlobalDisableExpiryTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetDailyLimitReachedSubscribersAsync(
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.IsDailyLimitExceeded())
                .OrderBy(x => x.TodayReceivedCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> GetSubscribersByPreferenceAsync(
            string languagePreference,
            string timeZonePreference,
            bool onlyEnabled = true,
            CancellationToken cancellationToken = default)
        {
            var queryable = await GetQueryableAsync();

            queryable = queryable
                .IncludeDetails()
                .Where(x => x.LanguagePreference == languagePreference &&
                           x.TimeZonePreference == timeZonePreference)
                .WhereIf(onlyEnabled, x => x.IsEnabled && !x.IsGloballyDisabled);

            return await queryable
                .OrderBy(x => x.UserName)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<NotificationSubscription?> GetByEmailAddressAsync(
            string emailAddress,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .FirstOrDefaultAsync(x => x.EmailAddress == emailAddress, GetCancellationToken(cancellationToken));
        }

        public async Task<NotificationSubscription?> GetByPhoneNumberAsync(
            string phoneNumber,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .FirstOrDefaultAsync(x => x.PhoneNumber == phoneNumber, GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationSubscription>> SearchAsync(
            string keyword,
            bool? isEnabled = null,
            int maxCount = 50,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.UserName.Contains(keyword) ||
                           x.EmailAddress!.Contains(keyword) ||
                           x.PhoneNumber!.Contains(keyword) ||
                           x.Tags.Contains(keyword))
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .OrderBy(x => x.UserName)
                .Take(maxCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<NotificationSubscriptionStatisticsDto> GetSubscriptionStatisticsAsync(
            CancellationToken cancellationToken = default)
        {
            var queryable = await GetQueryableAsync();

            var statistics = new NotificationSubscriptionStatisticsDto
            {
                TotalSubscriptions = await queryable.LongCountAsync(GetCancellationToken(cancellationToken)),
                EnabledCount = await queryable.CountAsync(x => x.IsEnabled, GetCancellationToken(cancellationToken)),
                DisabledCount = await queryable.CountAsync(x => !x.IsEnabled, GetCancellationToken(cancellationToken)),
                GlobalDisabledCount = await queryable.CountAsync(x => x.IsGloballyDisabled, GetCancellationToken(cancellationToken)),
                MarketingAllowedCount = await queryable.CountAsync(x => x.AllowMarketingNotifications, GetCancellationToken(cancellationToken)),
                SystemAllowedCount = await queryable.CountAsync(x => x.AllowSystemNotifications, GetCancellationToken(cancellationToken)),
                SecurityAllowedCount = await queryable.CountAsync(x => x.AllowSecurityNotifications, GetCancellationToken(cancellationToken))
            };

            // 按语言偏好统计
            statistics.ByLanguage = await queryable
                .GroupBy(x => x.LanguagePreference)
                .Select(g => new { Language = g.Key, Count = g.LongCount() })
                .ToDictionaryAsync(x => x.Language, x => x.Count, GetCancellationToken(cancellationToken));

            // 按时区偏好统计
            statistics.ByTimeZone = await queryable
                .GroupBy(x => x.TimeZonePreference)
                .Select(g => new { TimeZone = g.Key, Count = g.LongCount() })
                .ToDictionaryAsync(x => x.TimeZone, x => x.Count, GetCancellationToken(cancellationToken));

            // 按优先级统计
            statistics.ByPriority = await queryable
                .GroupBy(x => x.DefaultPriority)
                .Select(g => new { Priority = g.Key, Count = g.LongCount() })
                .ToDictionaryAsync(x => x.Priority, x => x.Count, GetCancellationToken(cancellationToken));

            // 计算活跃用户数
            var lastMonth = DateTime.UtcNow.AddDays(-30);
            statistics.ActiveUsersCount = await queryable
                .CountAsync(x => x.LastActiveTime.HasValue && x.LastActiveTime >= lastMonth, GetCancellationToken(cancellationToken));

            // 计算今日活跃用户数
            var today = DateTime.UtcNow.Date;
            statistics.TodayActiveCount = await queryable
                .CountAsync(x => x.LastActiveTime.HasValue && x.LastActiveTime.Value.Date == today, GetCancellationToken(cancellationToken));

            return statistics;
        }

        public async Task ResetDailyStatisticsAsync(
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();
            var subscriptions = await DbSet
                .Where(x => x.ShouldResetDailyStatistics())
                .ToListAsync(GetCancellationToken(cancellationToken));

            foreach (var subscription in subscriptions)
            {
                subscription.ResetTodayReceivedCount();
            }

            await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
        }

        public async Task UpdateLastActiveTimeAsync(
            Guid userId,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();
            var subscription = await DbSet
                .FirstOrDefaultAsync(x => x.UserId == userId, GetCancellationToken(cancellationToken));

            if (subscription != null)
            {
                subscription.UpdateLastActiveTime();
                await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
            }
        }

        public async Task<bool> IsUserSubscribedAsync(
            Guid userId,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .AnyAsync(x => x.UserId == userId, GetCancellationToken(cancellationToken));
        }

        public async Task<bool> CanReceiveNotificationAsync(
            Guid userId,
            NotificationType type,
            NotificationChannel channel,
            CancellationToken cancellationToken = default)
        {
            var subscription = await (await GetQueryableAsync())
                .FirstOrDefaultAsync(x => x.UserId == userId, GetCancellationToken(cancellationToken));

            return subscription?.CanReceiveNotification(type, channel) ?? false;
        }
    }

    /// <summary>
    /// 通知订阅统计DTO
    /// </summary>
    public class NotificationSubscriptionStatisticsDto
    {
        public long TotalSubscriptions { get; set; }
        public int EnabledCount { get; set; }
        public int DisabledCount { get; set; }
        public int GlobalDisabledCount { get; set; }
        public int MarketingAllowedCount { get; set; }
        public int SystemAllowedCount { get; set; }
        public int SecurityAllowedCount { get; set; }
        public int ActiveUsersCount { get; set; }
        public int TodayActiveCount { get; set; }

        public Dictionary<string, long> ByLanguage { get; set; } = new();
        public Dictionary<string, long> ByTimeZone { get; set; } = new();
        public Dictionary<NotificationPriority, long> ByPriority { get; set; } = new();

        public DateTime StatisticsTime { get; set; } = DateTime.UtcNow;
    }
}